package com.huawei.translate.api;

import com.huawei.translate.model.MsAccessToken;
import com.huawei.translate.persistence.MsAccessTokenRepository;
import com.huawei.translate.provider.*;
import com.huawei.translate.service.AuthenticationService;
import com.huawei.translate.service.TranslateRecordService;
import com.huawei.translate.vo.translate.*;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static org.springframework.web.bind.annotation.RequestMethod.POST;

@RestController
@RequestMapping("/api/translates")
@Api(value = "translate Service API", description = "Provide translate service")
public class TranslateApi {

    private static final Logger LOG = LoggerFactory.getLogger(TranslateApi.class);

    @Autowired
    private AuthenticationService authenticationService;

    @Autowired
    private TranslateRecordService translateRecordService;

    private TranslateProvider translateProvider;

    @Autowired
    private MSTranslateProvider msTranslateProvider;

    @Autowired
    private YoudaoTranslateProvider youdaoTranslateProvider;

    @Autowired
    private HuaWeiTranslateProvider huaWeiTranslateProvider;

    @Autowired
    private MsAccessTokenRepository msAccessTokenRepository;

    @Autowired
    private TranslateProviderFactory translateProviderFactory;

    @ApiOperation(value = "translateArray", httpMethod = "POST")
    @RequestMapping(method = POST, value = "/array")
    public ResponseEntity<TranslateArrayResponse> translateArray(@RequestBody TranslateArrayRequest request, @RequestHeader HttpHeaders headers) {

        long startTime = System.currentTimeMillis();
        TranslateArrayResponse response = new TranslateArrayResponse();

        if (!request.isValid()) {
            response.setCode(HttpStatus.BAD_REQUEST.value());
            return new ResponseEntity(response, HttpStatus.OK);
        }

        if (!authenticationService.isUserValid(request.getAccessToken())) {
            response.setCode(HttpStatus.FORBIDDEN.value());
            return new ResponseEntity(response, HttpStatus.OK);
        }

        LOG.info("authenticate took:" + (System.currentTimeMillis() - startTime) + " ms");

        List<String> result = msTranslateProvider.translate(request);

        if (result == null) {
            response.setCode(HttpStatus.BAD_REQUEST.value());
            return new ResponseEntity(response, HttpStatus.OK);
        }

        LOG.info("translate array took:" + (System.currentTimeMillis() - startTime) + " ms");
        response.setCode(HttpStatus.OK.value());
        response.setMessages(result);
        translateRecordService.record(request, result, msTranslateProvider.toDescription(), headers.getFirst("traceid"));
        return new ResponseEntity(response, HttpStatus.OK);
    }

    @ApiOperation(value = "translate", httpMethod = "POST")
    @RequestMapping(method = POST)
    public ResponseEntity<TranslateResponse> create(@RequestBody TranslateRequest request, @RequestHeader HttpHeaders headers) {
        long startTime = System.currentTimeMillis();
        TranslateResponse responseBody = new TranslateResponse();
        if (!request.isValid()) {
            responseBody.setCode(HttpStatus.BAD_REQUEST.value());
            return new ResponseEntity(responseBody, HttpStatus.BAD_REQUEST);
        }

        if (!authenticationService.isUserValid(request.getAccessToken())) {
            responseBody.setCode(HttpStatus.FORBIDDEN.value());
            return new ResponseEntity(responseBody, HttpStatus.FORBIDDEN);
        }

        LOG.info("authenticate took:" + (System.currentTimeMillis() - startTime) + " ms");

        translateProvider = translateProviderFactory.getProvider(request);

        LOG.info("Route Provider took:" + (System.currentTimeMillis() - startTime) + " ms");
        responseBody = translateProvider.translate(request);

        translateRecordService.record(request, responseBody, this.translateProvider.toDescription(), headers.getFirst("traceid"));

        if (responseBody == null) {
            return reTryTranslate(request, headers.getFirst("traceid"));
        }
        LOG.info("translate took:" + (System.currentTimeMillis() - startTime) + " ms");

        return new ResponseEntity(responseBody, HttpStatus.OK);
    }

    @ApiOperation(value = "Detect", httpMethod = "POST")
    @RequestMapping(method = POST, value = "/detect")
    public ResponseEntity<DetectResponse> detect(@RequestBody DetectRequest request) {
        DetectResponse responseBody = new DetectResponse();
        if (!request.isValid()) {
            responseBody.setCode(HttpStatus.BAD_REQUEST.value());
            return new ResponseEntity(responseBody, HttpStatus.BAD_REQUEST);
        }

        if (!authenticationService.isUserValid(request.getAccessToken())) {
            responseBody.setCode(HttpStatus.FORBIDDEN.value());
            return new ResponseEntity(responseBody, HttpStatus.FORBIDDEN);
        }

        String language = msTranslateProvider.detect(request.getContent());
        translateRecordService.recordDetect(request, language);

        if (language == null) {
            responseBody.setCode(HttpStatus.BAD_REQUEST.value());
            return new ResponseEntity(responseBody, HttpStatus.BAD_REQUEST);
        }
        responseBody.setCode(HttpStatus.OK.value());
        responseBody.setLanguage(language);
        return new ResponseEntity(responseBody, HttpStatus.OK);
    }

    @ApiOperation(value = "Compare", httpMethod = "POST")
    @RequestMapping(method = POST, value = "/compare")
    public ResponseEntity<TranslateCompareResponse> compare(@RequestBody TranslateCompareRequest request) {
        Map<String, String> microsoftResult = new HashMap<>();
        request.getMicrosoft().stream().forEach((key) -> {
            String result = msTranslateProvider.translate(request.getFrom(), request.getTo(), request.getContent(), key);
            if (request != null) {
                microsoftResult.put(key, result);
            }
        });

        Map<String, String> youdaoResult = new HashMap<>();
        request.getYoudao().stream().forEach((key) -> {
            String result = youdaoTranslateProvider.translate(request.getFrom(), request.getTo(), request.getContent(), key);
            if (request != null) {
                youdaoResult.put(key, result);
            }
        });

        Map<String, String> huaweiResult = new HashMap<>();
        request.getHuawei().stream().forEach((key) -> {
            String result = huaWeiTranslateProvider.translate(request.getFrom(), request.getTo(), request.getContent(), key);
            if (request != null) {
                huaweiResult.put(key, result);
            }
        });

        TranslateCompareResponse response = new TranslateCompareResponse();
        response.setMicrosoft(microsoftResult);
        response.setYoudao(youdaoResult);
        response.setHuawei(huaweiResult);
        return new ResponseEntity(response, HttpStatus.OK);
    }


    @ApiOperation(value = "REFRESH ACCESSTOKEN", httpMethod = "POST")
    @RequestMapping(method = POST, value = "/token")
    public ResponseEntity refreshAccessToken() {

        String accesstoken = msTranslateProvider.getAccessToken();
        LOG.info("Execute refreshAccessToken Job,Token is :" + accesstoken);

        int retryTimes = 0;

        while (accesstoken == null && retryTimes < 100) {
            accesstoken = msTranslateProvider.getAccessToken();
            LOG.info("Execute refreshAccessToken retry Job,Token is :" + accesstoken);
            retryTimes++;
        }

        if (accesstoken == null) {
            return new ResponseEntity(HttpStatus.EXPECTATION_FAILED);
        }

        if (msAccessTokenRepository.findAll().isEmpty()) {
            MsAccessToken token = new MsAccessToken();
            token.setAccessToken(accesstoken);
            token.setCreatedAt(new Date());
            token.setUpdatedAt(new Date());
            msAccessTokenRepository.save(token);
        } else {
            MsAccessToken msAccessToken = msAccessTokenRepository.findAllByOrderByUpdatedAtDesc().get(0);
            msAccessToken.setAccessToken(accesstoken);
            msAccessToken.setUpdatedAt(new Date());
            msAccessTokenRepository.save(msAccessToken);
        }

        return new ResponseEntity(HttpStatus.OK);
    }

    private ResponseEntity<TranslateResponse> reTryTranslate(TranslateRequest request, String traceId) {
        TranslateResponse responseBody = new TranslateResponse();

        if (isMsTranslateProvider()) {
            responseBody.setCode(HttpStatus.BAD_REQUEST.value());
            return new ResponseEntity(responseBody, HttpStatus.BAD_REQUEST);
        }

        responseBody = msTranslateProvider.translate(request);

        if (responseBody == null) {
            responseBody.setCode(HttpStatus.BAD_REQUEST.value());
            return new ResponseEntity(HttpStatus.BAD_REQUEST);
        }

        translateRecordService.record(request, responseBody, msTranslateProvider.toDescription(), traceId);
        responseBody.setCode(HttpStatus.OK.value());
        return new ResponseEntity(responseBody, HttpStatus.OK);
    }

    private boolean isMsTranslateProvider() {
        return this.translateProvider.toDescription() == TranslateProviderType.MS.getDescription();
    }
}
